#ifndef __FISHJOY_LOG_H__
#define __FISHJOY_LOG_H__

#include <cstdarg>
#include <cstdint>
#include <fstream>
#include <list>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

#include "fishjoy/thread/thread.hpp"
#include "fishjoy/utils/singleton.hpp"
#include "fishjoy/utils/util.hpp"

// TODO std::source_location https://en.cppreference.com/w/cpp/utility/source_location
// TODO 异步日志

/**
 * @brief 使用流式方式将日志级别 level 的日志写入到 logger
 */
#define FISHJOY_LOG_LEVEL(logger, level)                                                                                    \
  if (logger->getLevel() <= level)                                                                                          \
  fishjoy::LogEventWrap(fishjoy::LogEvent::ptr(new fishjoy::LogEvent(                                                       \
logger, level, __FILE__, __LINE__, 0, fishjoy::GetThreadId(), fishjoy::GetFiberId(), time(0), fishjoy::Thread::GetName()))) \
      .getSS()

/**
 * @brief 使用流式方式将日志级别 debug 的日志写入到 logger
 */
#define FISHJOY_LOG_DEBUG(logger) FISHJOY_LOG_LEVEL(logger, fishjoy::LogLevel::DEBUG)
/**
 * @brief 使用流式方式将日志级别 info 的日志写入到 logger
 */
#define FISHJOY_LOG_INFO(logger)  FISHJOY_LOG_LEVEL(logger, fishjoy::LogLevel::INFO)
/**
 * @brief 使用流式方式将日志级别 warn 的日志写入到 logger
 */
#define FISHJOY_LOG_WARN(logger)  FISHJOY_LOG_LEVEL(logger, fishjoy::LogLevel::WARN)
/**
 * @brief 使用流式方式将日志级别 error 的日志写入到 logger
 */
#define FISHJOY_LOG_ERROR(logger) FISHJOY_LOG_LEVEL(logger, fishjoy::LogLevel::ERROR)
/**
 * @brief 使用流式方式将日志级别 fatal 的日志写入到 logger
 */
#define FISHJOY_LOG_FATAL(logger) FISHJOY_LOG_LEVEL(logger, fishjoy::LogLevel::FATAL)

/**
 * @brief 使用格式化方式将日志级别 level 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_LEVEL(logger, level, fmt, ...)                                                                      \
  if (logger->getLevel() <= level)                                                                                          \
  fishjoy::LogEventWrap(fishjoy::LogEvent::ptr(new fishjoy::LogEvent(                                                       \
logger, level, __FILE__, __LINE__, 0, fishjoy::GetThreadId(), fishjoy::GetFiberId(), time(0), fishjoy::Thread::GetName()))) \
      .getEvent()                                                                                                           \
      ->format(fmt, __VA_ARGS__)

/**
 * @brief 使用格式化方式将日志级别 debug 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_DEBUG(logger, fmt, ...) FISHJOY_LOG_FMT_LEVEL(logger, fishjoy::LogLevel::DEBUG, fmt, __VA_ARGS__)
/**
 * @brief 使用格式化方式将日志级别 info 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_INFO(logger, fmt, ...)  FISHJOY_LOG_FMT_LEVEL(logger, fishjoy::LogLevel::INFO, fmt, __VA_ARGS__)
/**
 * @brief 使用格式化方式将日志级别 warn 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_WARN(logger, fmt, ...)  FISHJOY_LOG_FMT_LEVEL(logger, fishjoy::LogLevel::WARN, fmt, __VA_ARGS__)
/**
 * @brief 使用格式化方式将日志级别 fmt 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_ERROR(logger, fmt, ...) FISHJOY_LOG_FMT_LEVEL(logger, fishjoy::LogLevel::ERROR, fmt, __VA_ARGS__)
/**
 * @brief 使用格式化方式将日志级别 fatal 的日志写入到 logger
 */
#define FISHJOY_LOG_FMT_FATAL(logger, fmt, ...) FISHJOY_LOG_FMT_LEVEL(logger, fishjoy::LogLevel::FATAL, fmt, __VA_ARGS__)
/**
 * @brief 获取主日志器
 */
#define FISHJOY_LOG_ROOT()     fishjoy::LoggerMgr::GetInstance()->getRoot()
/**
 * @brief 获取 name 日志器
 */
#define FISHJOY_LOG_NAME(name) fishjoy::LoggerMgr::GetInstance()->getLogger(name)

namespace fishjoy {

  class Logger;
  class LoggerManager;

  /**
 * @brief 日志级别
 */
  class LogLevel {
   public:
    /**
     * @brief 日志级别枚举
     */
    enum Level { UNKNOWN = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4, FATAL = 5 };

    /**
     * @brief 将日志级别转成文本输出
     * @param[in] level 日志级别
     */
    static const char* ToString(LogLevel::Level level);

    /**
     * @brief 将文本转换成日志级别
     * @param[in] str 日志级别文本
     */
    static LogLevel::Level FromString(const std::string& str);
  };

  /**
    * @brief 日志事件
   */
  class LogEvent {
   public:
    using ptr = std::shared_ptr<LogEvent>;

    /**
     * @brief 构造函数
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] file 文件名
     * @param[in] line 文件行号
     * @param[in] elapse 程序启动依赖的耗时(毫秒)
     * @param[in] thread_id 线程id
     * @param[in] fiber_id 协程id
     * @param[in] time 日志事件(秒)
     * @param[in] thread_name 线程名称
     */
    LogEvent(
        std::shared_ptr<Logger> logger,
        LogLevel::Level level,
        const char* file,
        int32_t m_line,
        uint32_t elapse,
        pid_t thread_id,
        uint32_t fiber_id,
        time_t time,
        const std::string& thread_name);

    /**
     * @brief 返回文件名
     */
    const char* getFile() const { return m_file; }

    /**
     * @brief 返回行号
     */
    int32_t getLine() const { return m_line; }

    /**
     * @brief 返回耗时
     */
    uint32_t getElapse() const { return m_elapse; }

    /**
     * @brief 返回线程ID
     */
    pid_t getThreadId() const { return m_threadId; }
    /**
     * @brief 返回协程ID
     */
    uint32_t getFiberId() const { return m_fiberId; }
    /**
     * @brief 返回时间
     */
    time_t getTime() const { return m_time; }
    /**
     * @brief 返回日志内容
     */
    std::string getContent() const { return m_ss.str(); }
    /**
     * @brief 返回日志器
     */
    std::shared_ptr<Logger> getLogger() const { return m_logger; }
    /**
     * @brief 返回日志级别
     */
    LogLevel::Level getLevel() const { return m_level; }
    /**
     * @brief 返回线程名称
     */
    const std::string& getThreadName() const { return m_threadName;}
    /**
     * @brief 返回日志内容字符串流
     */
    std::stringstream& getSS() { return m_ss; }
    /**
     * @brief 格式化写入日志内容
     */
    void format(const char* fmt, ...);
    /**
     * @brief 格式化写入日志内容
     */
    void format(const char* fmt, va_list al);

   private:
    /// 文件名
    const char* m_file = nullptr;
    /// 行号
    int32_t m_line = 0;
    /// 程序启动开始到现在的毫秒数
    uint32_t m_elapse = 0;
    /// 线程 Id
    pid_t m_threadId = 0;
    /// 协程 Id
    uint32_t m_fiberId = 0;
    /// 时间戳
    time_t m_time = 0;
    /// 线程名称
    std::string m_threadName;
    /// 日志内容流
    std::stringstream m_ss;
    /// 日志器
    std::shared_ptr<Logger> m_logger;
    /// 日志等级
    LogLevel::Level m_level;
  };

  /**
    * @brief 日志事件包装器
   */
  class LogEventWrap {
   public:
    /**
     * @brief 构造函数
     * @param[in] event 日志事件
     */
    explicit LogEventWrap(LogEvent::ptr event);

    /**
     * @brief 析构函数
     */
    ~LogEventWrap();

    /**
     * @brief 获取日志事件
     */
    LogEvent::ptr getEvent() const { return m_event; }

    /**
     * @brief 获取日志内容流
     */
    std::stringstream& getSS();

   private:
    /**
     * @brief 日志事件
     */
    LogEvent::ptr m_event;
  };

  /**
   * @brief 日志格式器
   */
  class LogFormatter {
   public:
    using ptr = std::shared_ptr<LogFormatter>;

    /**
     * @brief 构造函数
     * @param[in] pattern 格式模板
     * @details
     *  %m 消息
     *  %p 日志级别
     *  %r 累计毫秒数
     *  %c 日志名称
     *  %t 线程id
     *  %n 换行
     *  %d 时间
     *  %f 文件名
     *  %l 行号
     *  %T 制表符
     *  %F 协程id
     *  %N 线程名称
     *
     *  默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
     */
    explicit LogFormatter(const std::string& pattern);

    /**
     * @brief 返回格式化日志文本
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     */
    std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);

   public:
    /**
     * @brief 日志内容项格式化
     */
    class FormatItem {
     public:
      using ptr = std::shared_ptr<FormatItem>;
      /**
         * @brief 析构函数
       */
      virtual ~FormatItem() = default;

      /**
         * @brief 格式化日志到流
         * @param[in, out] os 日志输出流
         * @param[in] logger 日志器
         * @param[in] level 日志等级
         * @param[in] event 日志事件
       */
      virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    };

    /**
     * @brief 初始化,解析日志模板
     */
    void init();

    /**
     * @brief 是否有错误
     */
    bool isError() const { return m_error; }

    /**
     * @brief 返回日志模板
     */
    std::string getPattern() const { return m_pattern; }

   private:
    /// 日志格式模板
    std::string m_pattern;
    /// 日志格式解析后格式
    std::vector<FormatItem::ptr> m_items;
    /// 是否有错误
    bool m_error = false;
  };

  /**
 * @brief 日志输出目标
   */
  class LogAppender {
    friend class Logger;

   public:
    using ptr = std::shared_ptr<LogAppender>;
    using MutexType = Spinlock;

    /**
     * @brief 析构函数
     */
    virtual ~LogAppender() = default;

    /**
     * @brief 写入日志
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     */
    virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;

    /**
     * @brief 将日志输出目标的配置转成YAML String
     */
    virtual std::string toYamlString() = 0;

    /**
     * @brief 更改日志格式器
     */
    void setFormatter(LogFormatter::ptr val);

    /**
     * @brief 获取日志格式器
     */
    LogFormatter::ptr getFormatter();

    /**
     * @brief 获取日志级别
     */
    LogLevel::Level getLevel() const { return m_level; }

    /**
     * @brief 设置日志级别
     */
    void setLevel(LogLevel::Level val) { m_level = val; }

   protected:
    /// 日志级别 默认 Debug
    LogLevel::Level m_level = LogLevel::DEBUG;
    /// 是否有自己的日志格式器
    bool m_hasFormatter = false;
    /// Mutex
    MutexType m_mutex;
    /// 日志格式器
    LogFormatter::ptr m_formatter;
  };

  /**
    * @brief 日志器
   */
  class Logger : public std::enable_shared_from_this<Logger> {
    friend class LoggerManager;
    using MutexType = Spinlock;

   public:
    using ptr = std::shared_ptr<Logger>;
    using Mutex = Spinlock;

    /**
     * @brief 构造函数
     * @param[in] name 日志器名称
     */
    explicit Logger(const std::string& name = "root");

    /**
     * @brief 写日志
     * @param[in] level 日志级别
     * @param[in] event 日志事件
     * @details 日志级别需要大于等于日志器级别才能被输出
     */
    void log(LogLevel::Level level, LogEvent::ptr event);

    /**
     * @brief 写debug级别日志
     * @param[in] event 日志事件
     */
    void debug(LogEvent::ptr event);
    /**
     * @brief 写info级别日志
     * @param[in] event 日志事件
     */
    void info(LogEvent::ptr event);
    /**
     * @brief 写warn级别日志
     * @param[in] event 日志事件
     */
    void warn(LogEvent::ptr event);
    /**
     * @brief 写error级别日志
     * @param[in] event 日志事件
     */
    void error(LogEvent::ptr event);
    /**
     * @brief 写fatal级别日志
     * @param[in] event 日志事件
     */
    void fatal(LogEvent::ptr event);

    /**
     * @brief 添加日志目标
     * @param[in] appender 日志目标
     */
    void addAppender(LogAppender::ptr appender);
    /**
     * @brief 删除日志目标
     * @param[in] appender 日志目标
     */
    void delAppender(LogAppender::ptr appender);
    /**
     * @brief 清空日志目标
     */
    void clearAppenders();


    /**
     * @brief 获取日志目标点集合
     */
    std::list<LogAppender::ptr> getAppenders();
    /**
     * 获取日志器级别
     * @return 日志器级别
     */
    LogLevel::Level getLevel() const { return m_level; }

    /**
     * 设置日志器级别
     * @param val 日志器级别
     */
    void setLevel(LogLevel::Level val) { m_level = val; }

    /**
     * 获取日志器名称
     * @return 日志器名称
     */
    const std::string& getName() const { return m_name; }

    /**
     * @brief LogFormatter::ptr 设置日志器格式
     * @param val 日志格式器的 shared_ptr
     */
    void setFormatter(LogFormatter::ptr val);

    /**
     * @brief 通过字符串设置日志器格式
     * @param val 日志格式器字符串模板
     */
    void setFormatter(const std::string& val);

    /**
     * @brief 获取日志器的日志格式器
     * @return 指向日志器的日志格式器的 shared_ptr
     */
    LogFormatter::ptr getFormatter();

    /**
     * @brief 获取日志器 YAML 配置的字符串形式
     * @return YAML 配置的字符串形式
     */
    std::string toYamlString();

   private:
    ///日志名称
    std::string m_name;
    ///日志级别
    LogLevel::Level m_level;
    /// 输出目标点集合
    std::list<LogAppender::ptr> m_appenders;
    /// 日志格式器
    LogFormatter::ptr m_formatter;
    /// 主日志
    Logger::ptr m_root;
    /// 自旋锁
    MutexType m_mutex;
  };

  /**
   * @brief 输出到控制台的Appender
   */
  class StdoutLogAppender : public LogAppender {
   public:
    using ptr = std::shared_ptr<StdoutLogAppender>;

    /**
     * 输出到控制台
     * @param logger 日志器
     * @param level 日志级别
     * @param event 日志事件
     */
    void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;

    /**
     * 获取 StdoutLogAppender YAML 形式的配置，并以字符串返回
     * @return YAML 配置的字符串形式
     */
    std::string toYamlString() override;
  };

  /**
   *@ brief 输出到文件的 Appender
   */
  class FileLogAppender : public LogAppender {
   public:
    using ptr = std::shared_ptr<FileLogAppender>;

    /**
     * @brief 构造函数
     * @param filename 输出文件路径
     */
    explicit FileLogAppender(const std::string& filepath);

    /**
     * 日志输出到文件
     * @param logger 日志器
     * @param level 日志级别
     * @param event 日志时间
     */
    void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;

    /**
     * 获取 FileLogAppender YAML 形式的配置，并以字符串返回
     * @return YAML 配置的字符串形式
     */
    std::string toYamlString() override;

    /**
     * @brief 重新打开文件
     * @return 文件打开成功返回true
     */
    bool reopen();

   private:
    /// 输出文件路径
    std::string m_filepath;

    /// 输出文件的输出流
    std::ofstream m_filestream;

    /// 最后修改时间
    time_t m_lastTime = 0;
  };

  /**
   * @brief 日志管理器
   */
  class LoggerManager {
   public:

    /**
     * @brief 构造函数
     */
    LoggerManager();

    /**
     * 根据名称获取日志器
     * @param name 名称
     * @return 日志器的 shared_ptr
     */
    Logger::ptr getLogger(const std::string& name);
    using MutexType = Spinlock;

    /**
     * @brief 日志管理器来进行初始化
     */
    void init();

    /**
     * @brief 获取主日志器
     * @return 指向主日志器的 shared_ptr
     */
    Logger::ptr getRoot() const { return m_root; }

    /**
     * @brief 获取 日志管理器 YAML 形式的配置，并以字符串返回
     * @return 日志管理器 YAML 配置的字符串形式
     */
    std::string toYamlString();

   private:
    /// 日志器集合，日志名映射到日志器的 shared_ptr
    std::map<std::string, Logger::ptr> m_loggers;
    ///主日志器
    Logger::ptr m_root;
    ///自旋锁
    MutexType m_mutex;
  };

  /// 日志器管理类单例模式
  using LoggerMgr = fishjoy::Singleton<LoggerManager>;

}  // namespace fishjoy

#endif